//Random number generator in the range [-1,1]
float randomNumberGenerator(int x, int y){
	int n = (int)(x * 40.0 + y * 6400.0);
	n = (n << 13) ^ n;
	return 1.0f - (float)( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f;
}



//Kernel that init the crowd uniformly in the area specified
/*
 *  0---------------------(area_size_x)
 *  |                      |
 *  |                      |
 *  |                      |
 *  |                      |
 *  (area_size_y)-----------
 */
kernel void initCrowd(	global float4 *agents,
						global float4 *velocity,
						global float *maxVelocity,
						global float *maxAcceleration,
						global float* leaderMovement,
						int numLeaderMovement,
						global float* particleMovement,
						int numParticleMovement,
						global float* flocking,
						global float4* groupingOptions,
						global float4* movementOptions,
						global float* groupDefinition,
						int numDefinedGroups,
						global float2 *originalID,
						int agents_mesh_width,
						int area_size_x,
						int area_size_y) {

    unsigned int x = get_global_id(0);
    unsigned int y = get_global_id(1);
    
    int numAgents = agents_mesh_width * agents_mesh_width;
    int particleID = x * y;
    
    if( particleID > numAgents)
    	return;
    	
    particleID = y*agents_mesh_width + x;

    // calculate uv coordinates
    float u = (x % agents_mesh_width) * (area_size_x / (float) agents_mesh_width);
    float v = (y % agents_mesh_width) * (area_size_y / (float) agents_mesh_width);
    
    //
    float currentGroupStart = 0;
    float particleCount = 0;
    float myGroup = -1.0;
    float myGroupSize = 0.0f;
    float isLeader 		= 0.0f;
    for(int i=0; i<numDefinedGroups; i++){
    	float groupSize 		= groupDefinition[i*2 + 0];
    	float groupOccurrence 	= groupDefinition[i*2 + 1];
    	
    	if(groupOccurrence < 0){
    		groupOccurrence = -1 * (groupOccurrence / 100);
    		float particlesInGroup = numAgents * groupOccurrence;
    		particlesInGroup = round(particlesInGroup);
    		float numberOfGroups = particlesInGroup / groupSize;
    		numberOfGroups = ceil(numberOfGroups);
    		
    		if(particleID > particleCount && particleID < particleCount + particlesInGroup){
    			int particleDif = (particleID - particleCount);
    			int modulus = (particleDif % (int)groupSize);
    			if(modulus == 0)
    				isLeader = 1.0f;
    			float groupOffset = particleDif - modulus;
    			groupOffset /= groupSize;
    			myGroup = currentGroupStart + groupOffset;
    			myGroupSize = groupSize;
    			break;
    		}else{
    			particleCount += particlesInGroup;
    			currentGroupStart += numberOfGroups;
    			continue;
    		}
    	}else{
    	}
    }
    
    float meanVel		= 0.0f;
    float stdDevVel 	= 0.0f;
    float maxAccel 		= 0.0f;
    float seek 			= 0.0f;
    float wander 		= 0.0f;
    
    float separation 	= flocking[0];
    float cohesion 		= flocking[1];
    float alignment 	= flocking[2];
    
    if(isLeader == 1.0f){
    	int leaderMovementIndex = (particleID) % numLeaderMovement;
    	meanVel 	= leaderMovement[leaderMovementIndex*5 + 0];
    	stdDevVel 	= leaderMovement[leaderMovementIndex*5 + 1];
    	maxAccel 	= leaderMovement[leaderMovementIndex*5 + 2];
    	seek 		= leaderMovement[leaderMovementIndex*5 + 3];
    	wander 		= leaderMovement[leaderMovementIndex*5 + 4];
    }else{
    	int particleMovementIndex = (particleID) % numParticleMovement;
    	meanVel		= particleMovement[particleMovementIndex*5 + 0];
    	stdDevVel	= particleMovement[particleMovementIndex*5 + 1];
    	maxAccel	= particleMovement[particleMovementIndex*5 + 2];
    	seek		= particleMovement[particleMovementIndex*5 + 3];
    	wander		= particleMovement[particleMovementIndex*5 + 4];
    }

    // write output vertex
    agents[y*agents_mesh_width + x]   = (float4)(u,    0.0f, v,    1.0f);
    velocity[y*agents_mesh_width + x] = (float4)(0.0f, 0.0f, 0.0f, 0.0f);
    float randomNumber = randomNumberGenerator(x,y);
    if(randomNumber > 1)
    	randomNumber = 1;
    if(randomNumber < -1)
    	randomNumber = -1;
    maxVelocity[y*agents_mesh_width + x] = meanVel + stdDevVel*randomNumber;
    maxAcceleration[y*agents_mesh_width + x] = maxAccel;
    groupingOptions[y*agents_mesh_width + x] = (float4)(isLeader, myGroup, myGroupSize, wander);
    movementOptions[y*agents_mesh_width + x]=(float4)(separation, cohesion, alignment, seek);
    originalID[y*agents_mesh_width + x] = (float2)(x, y);
}